/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.ext.calendar.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;

import org.zkoss.zul.event.ListDataEvent;

import cn.easyplatform.lang.Lang;
import cn.easyplatform.web.ext.calendar.Calendars;
import cn.easyplatform.web.ext.calendar.api.CalendarEvent;
import cn.easyplatform.web.ext.calendar.api.CalendarModel;
import cn.easyplatform.web.ext.calendar.api.RenderContext;
import cn.easyplatform.web.ext.calendar.event.CalendarDataEvent;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class SimpleCalendarModel extends AbstractCalendarModel implements
		Serializable {
	private static final long serialVersionUID = 20090320175712L;

	protected List<CalendarEvent> _list;

	/**
	 * Constructor
	 * 
	 * @param list
	 *            the list to represent
	 * @param live
	 *            whether to have a 'live' {@link CalendarModel} on top of the
	 *            specified list. If false, the content of the specified list is
	 *            copied. If true, this object is a 'facade' of the specified
	 *            list, i.e., when you add or remove items from this
	 *            SimpleCalendarModel, the inner "live" list would be changed
	 *            accordingly.
	 * 
	 *            However, it is not a good idea to modify <code>list</code> if
	 *            it is passed to this method with live is true, since
	 *            {@link Calendars} is not smart enough to handle it. Instead,
	 *            modify it thru this object.
	 */
	public SimpleCalendarModel(List<CalendarEvent> list, boolean live) {
		_list = live ? list : new ArrayList<CalendarEvent>(list);
	}

	/**
	 * Constructor.
	 */
	public SimpleCalendarModel() {
		_list = new ArrayList<CalendarEvent>();
	}

	/**
	 * Constructor. It makes a copy of the specified collection (i.e., not
	 * live).
	 */
	public SimpleCalendarModel(Collection<CalendarEvent> c) {
		_list = new ArrayList<CalendarEvent>(c);
	}

	/**
	 * Constructor. It makes a copy of the specified array (i.e., not live).
	 */
	public SimpleCalendarModel(CalendarEvent[] array) {
		_list = Arrays.asList(array);
	}

	/**
	 * Constructor.
	 * 
	 * @param initialCapacity
	 *            the initial capacity for this SimpleCalendarModel.
	 */
	public SimpleCalendarModel(int initialCapacity) {
		_list = new ArrayList<CalendarEvent>(initialCapacity);
	}

	/**
	 * Adds the calendar event to the specified index of the list.
	 */
	public void add(int index, CalendarEvent e) {
		if (e == null)
			throw new NullPointerException("CalendarEvent cannot be null!");
		_list.add(index, e);
		fireEvent(CalendarDataEvent.INTERVAL_ADDED, e);
	}

	/**
	 * Adds the calendar event to the list.
	 */
	public boolean add(CalendarEvent e) {
		if (e == null)
			throw new NullPointerException("CalendarEvent cannot be null!");
		boolean ret = _list.add(e);
		fireEvent(CalendarDataEvent.INTERVAL_ADDED, e);
		return ret;
	}

	/**
	 * Update the calendar event to the list.
	 */
	public boolean update(CalendarEvent e) {
		if (e == null)
			throw new NullPointerException("CalendarEvent cannot be null!");
		fireEvent(CalendarDataEvent.CONTENTS_CHANGED, e);
		boolean ret = _list.remove(e);
		if (!ret)
			return ret;
		ret = _list.add(e);
		return ret;
	}

	/**
	 * Removes the calendar event from the specified index.
	 */
	public CalendarEvent remove(int index) {
		CalendarEvent ret = (CalendarEvent) _list.remove(index);
		fireEvent(CalendarDataEvent.INTERVAL_REMOVED, ret);
		return ret;
	}

	/**
	 * Returns the index of the first occurrence of the specified element in
	 * this list.
	 */
	public int indexOf(CalendarEvent elem) {
		return _list.indexOf(elem);
	}

	/**
	 * Removes from the specified calendar event.
	 */
	public boolean remove(CalendarEvent e) {
		if (e == null)
			throw new NullPointerException("CalendarEvent cannot be null!");
		int index = indexOf(e);
		if (index >= 0) {
			remove(index);
		}
		return false;
	}

	/**
	 * Removes all of the elements from this list (optional operation). The list
	 * will be empty after this call returns.
	 */
	public void clear() {
		if (_list.isEmpty())
			return;

		_list.clear();
		fireEvent(ListDataEvent.INTERVAL_REMOVED, null, TimeZone.getDefault());
	}

	/**
	 * Returns the number of elements in this list.
	 */
	public int size() {
		return _list.size();
	}

	/**
	 * Returns the list that must be a list of {@link CalendarEvent} type.
	 * 
	 * @param beginDate
	 *            the begin date
	 * @param endDate
	 *            the end date
	 * @param rc
	 *            a RenderContext encapsulates the information needed for
	 *            Calendars.
	 */
	public List<CalendarEvent> get(Date beginDate, Date endDate,
			RenderContext rc) {
		List<CalendarEvent> list = new LinkedList<CalendarEvent>();
		long begin = beginDate.getTime();
		long end = endDate.getTime();

		for (Iterator<CalendarEvent> it = _list.iterator(); it.hasNext();) {
			CalendarEvent ce = (CalendarEvent) it.next();
			long b = ce.getBeginDate().getTime();
			long e = ce.getEndDate().getTime();

			if (e <= begin || b >= end)
				continue;

			list.add(ce);
		}
		return list;
	}

	@Override
	public CalendarEvent getEventById(Object id) {
		for (Iterator<CalendarEvent> it = _list.iterator(); it.hasNext();) {
			CalendarEvent ce = (CalendarEvent) it.next();
			if (Lang.equals(ce.getId(), id))
				return ce;
		}
		return null;
	}

}
